#include "LogMessage.hpp"
#include "Thread.hpp"

#include <iostream>

#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>

const int BUFF_SIZE = 512;

// 服务端IP+port
sockaddr_in sendTo; 
socklen_t lenS = sizeof(sendTo);


void* SendMes(void* args)
{
    std::string name = ((Thread_name_and_Args*)args)->_name;
    int sock = *(int*)((Thread_name_and_Args*)args)->_args;

    // 发送数据
    while(1)
    {
        // 用户输入数据
        std::string message;
        std::cerr << "请输入你要发送的数据 ::";
        std::getline(std::cin, message);

        // 如果用户输入quit就退出客户端
        if(message == "quit") break;

        // 发送
        sendto(sock, message.c_str(), message.size(), 0, reinterpret_cast<sockaddr*>(&sendTo), lenS);
    }

    exit(10);
}

void* RecvMes(void* args)
{
    std::string name = ((Thread_name_and_Args*)args)->_name;
    int sock = *(int*)((Thread_name_and_Args*)args)->_args;

    // 接收数据
    while (1)
    {
        char buffer[BUFF_SIZE] = {0};
        sockaddr_in get_IP_port; // 占位
        socklen_t lenG = sizeof(get_IP_port);

        ssize_t res = recvfrom(sock, buffer, BUFF_SIZE, 0, reinterpret_cast<sockaddr*>(&get_IP_port), &lenG);
        if(res > 0)
        {
            buffer[res] = 0;
            std::cout << std::endl;
            std::cout << buffer << std::endl;
        }
    }
}


// 这里客户端的用法是输入服务器的IP和端口号
void usage(const char* fileName)
{
    printf("usage :\n%s IP port\n", fileName);
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        exit(1);
    }

    // 获取IP，该IP为服务端的IP
    in_addr_t serverIP = inet_addr(argv[1]);
    // 获取端口，该端口为服务端进程的端口
    in_port_t serverPort = htons(atoi(argv[2]));

    // 找到服务端的IP和端口
    // 把服务端的IP和端口填好
    memset(&sendTo, 0, lenS);
    sendTo.sin_family = AF_INET;
    sendTo.sin_addr.s_addr = serverIP;
    sendTo.sin_port = serverPort;

    // 创建套接字，和服务器中的选项都一样
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        LogMessage(FATAL, _F, _L, "client create socket fail :: %s", strerror(errno));
        exit(2);
    }

    Thread sender("sender", SendMes, &sock);
    Thread recver("recver", RecvMes, &sock);

    sender.CreateThread();
    recver.CreateThread();

    sender.JoinThread();
    recver.JoinThread();

    
    // 通信结束，关闭套接字所开的文件
    close(sock);

    return 0;
}

// const int BUFF_SIZE = 512;

// // 这里客户端的用法是输入服务器的IP和端口号
// void usage(const char* fileName)
// {
//     printf("usage :\n%s IP port\n", fileName);
// }

// int main(int argc, char* argv[])
// {
//     if(argc != 3)
//     {
//         usage(argv[0]);
//         exit(1);
//     }

//     // 获取IP，该IP为服务端的IP
//     in_addr_t serverIP = inet_addr(argv[1]);
//     // 获取端口，该端口为服务端进程的端口
//     in_port_t serverPort = htons(atoi(argv[2]));

//     // 找到服务端的IP和端口
//     sockaddr_in sendTo; 
//     socklen_t lenS = sizeof(sendTo);
//     // 把服务端的IP和端口填好
//     memset(&sendTo, 0, lenS);
//     sendTo.sin_family = AF_INET;
//     sendTo.sin_addr.s_addr = serverIP;
//     sendTo.sin_port = serverPort;

//     // 创建套接字，和服务器中的选项都一样
//     int sock = socket(AF_INET, SOCK_DGRAM, 0);
//     if(sock < 0)
//     {
//         LogMessage(FATAL, _F, _L, "client create socket fail :: %s", strerror(errno));
//         exit(2);
//     }

//     char buffer[BUFF_SIZE] = {0};
    
//     // 进行通信
//     while (1)
//     {
//         // 用户输入数据
//         std::string message;
//         printf("请输入你要发送的数据 ::");
//         std::getline(std::cin, message);

//         // 如果用户输入quit就退出客户端
//         if(message == "quit") break;

//         // 发送
//         sendto(sock, message.c_str(), message.size(), 0, reinterpret_cast<sockaddr*>(&sendTo), lenS);

//         // 用户接收服务端发回来的数据
//             /* 我们这里的逻辑已经知道了是唯一一个服务端在发送数据，而且服务端的IP和端口
//             号已经知道了，所以是不需要再用sockaddr_in接收发送数据方的IP和端口了。*/

//             /* 但是有些场景下会出现UDP的客户端既作为其他客户端的服务端，又作为一个服务端的
//             客户端的场景，所以这里的接口还是保留了这个recvfrom，而非为客户端直接新整
//             一个光接收数据不接受发送方IP和port的接口。*/

//             /* 不过我们这里还是需要写一个sockaddr_in来作为占位的字段，不能直接传给倒数第二个
//             参数传nullptr，不然会出现意想不到的问题*/
//         sockaddr_in get_IP_port;
//         socklen_t lenG = sizeof(get_IP_port);
//         ssize_t res = recvfrom(sock, buffer, BUFF_SIZE, 0, reinterpret_cast<sockaddr*>(&get_IP_port), &lenG);
//         if(res > 0)
//         {
//             buffer[res] = 0;
//             printf("server echo ::%s\n", buffer);
//         }
//     }


//     // 通信结束，关闭套接字所开的文件
//     close(sock);

//     return 0;
// }